﻿using BrowserCollect;
using Microsoft.Playwright;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace Common
{
    public class GoogleMapSearchCollectScript : ICollectScript
    {
        public async Task<string> GetHtml(string url, IPage page)
        {
            var response = await page.GotoAsync(url);
            var result = new List<MerchantItem>();
            var items = ScrapeInfo(await response.TextAsync());
            result.AddRange(items);

            var start = 0;
            var step = 100;
            var feedEl = page.Locator("[role=\"feed\"]");
            var responses = new List<IResponse>();

            EventHandler<IResponse> listResponse = (object e, IResponse res) =>
            {
                if (res.Url.StartsWith("https://www.google.com/search"))
                    responses.Add(res);
            };
            page.Response += listResponse;
            var scrollHeight = await feedEl.EvaluateAsync<int>("node => node.scrollHeight");
            while (start < scrollHeight)
            {
                while (start < scrollHeight)
                {
                    start += step;
                    await feedEl.EvaluateAsync($"node => node.scroll(0, {start})");
                    await Task.Delay(100);
                }
                scrollHeight = await feedEl.EvaluateAsync<int>("node => node.scrollHeight");
            }
            page.Response -= listResponse;

            foreach (var item in responses)
            {
                items = ScrapeInfo(await item.TextAsync(), true);
                result.AddRange(items);
            }

            return JsonSerializer.Serialize(result, new JsonSerializerOptions() { });

            IEnumerable<MerchantItem> ScrapeInfo(Sy.String html, bool removeSlash = false)
            {
                foreach (var chunk in html.Replace("\\\\", "\\").Replace("\\\"", "\"").Replace("\\/", "/").Split("https://search.google.com/local/reviews").Skip(1))
                {
                    var str = chunk.After("]").After("],");
                    str = AfterNComma(str, 2);
                    var website = string.Empty;
                    if (str[0] == '[')
                    {
                        website = str.After(",\"").BeforeFirst("\"");
                        str = str.After("],");
                    } 
                    else
                    {
                        str = str.After(",");
                    }

                    str = str.After("[null,null,");
                    var geo = str.BeforeFirst("]");

                    str = str.After("]").After(",").After(",");
                    var name = str.Mid("\"", "\"").Replace("\\", "");

                    str = AfterNComma(str, 7);
                    var address = str.Mid("\"", "\"").Replace("\\", "");

                    var phone = str.Contains("tel:") ? str.Mid("tel:", "\"").Replace("\\", "") : (Sy.String)string.Empty;
                    yield return new MerchantItem
                    {
                        Geo = geo,
                        Name = name,
                        Address = address,
                        Phone = phone,
                        Website = website,
                    };
                }
            }
        }

        private Sy.String AfterNComma(Sy.String str, int n)
        {
            var pointer = 0;
            var stack = new Stack<char>();
            var specialChars = new Dictionary<char, char>()
            {
                { '[', ']' },
            };
            while (n > 0)
            {
                var ch = str[pointer];
                var last = stack.LastOrDefault();

                if (specialChars.ContainsKey(ch))
                {
                    stack.Push(specialChars[ch]);
                }
                else if (ch == last)
                {
                    stack.Pop();
                }
                else if (ch == ',')
                {
                    n--;
                }
                pointer++;
            }

            return str.Substring(pointer);
        }

        public bool IsMatch(string url) => url.StartsWith("https://www.google.com/maps/search/");
    }

    public class MerchantItem
    {
        public string Name { get; set; }
        public string Geo { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
        public string Website { get; set; }
    }
}
